/*
 * Copyright (c) 2025 Huawei Device, Inc. Ltd. and <马弓手>.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "SQLiteConnectorDatabase.h"
#include <bundle/native_interface_bundle.h>
#include "Application.h"
#include "hilog/log.h"
#include "cJSON.h"

const unsigned int SQliteConnectorDatabase::LOG_PRINT_DOMAIN = 0xFF00;

bool SQliteConnectorDatabase::open(string fileName) {
    m_fileName = fileName;
    OH_NativeBundle_ApplicationInfo application = OH_NativeBundle_GetCurrentApplicationInfo();
    // 创建OH_Rdb_Config对象
    OH_Rdb_Config config;
    // 该路径为应用沙箱路径,直接指定
    config.dataBaseDir = "/data/storage/el2/database";//Application::g_databaseDir.c_str();
    // 数据库文件名
    config.storeName = fileName.c_str();
    // 应用包名
    config.bundleName = application.bundleName;
    // 应用模块名
    config.moduleName = application.bundleName;
    // 数据库文件安全等级
    config.securityLevel = OH_Rdb_SecurityLevel::S1;
    // 数据库是否加密
    config.isEncrypt = false;
    // config所占内存大小
    config.selfSize = sizeof(OH_Rdb_Config);
    // 数据库文件存放的安全区域
    config.area = RDB_SECURITY_AREA_EL1;
    
    int errCode = 0;
    // 获取获取OH_Rdb_Store实例
    string path = config.dataBaseDir;
    path += "/"+fileName;
    int rc = sqlite3_open(path.c_str(), &m_db);
    if(rc != SQLITE_OK) {
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "SQliteConnectorDatabase sqlite3_open return %d", rc);
        return false;
    }
    return true;
}

void SQliteConnectorDatabase::closeDatabaseNow() {
    sqlite3_close(m_db);
}

void SQliteConnectorDatabase::deleteDatbaseNow() 
{
    
}
void SQliteConnectorDatabase::executeSqlBatch(const vector<string>& queryarr, const vector<cJSON*>& jsonParams, CallbackContext cbc)
{
    if(m_db == NULL) {
        cbc.error("database has been closed");
        return;
    }
    
    int len = queryarr.size();
    cJSON* batchResults = cJSON_CreateArray();
    for(int i=0; i<len; i++) {
        int rowsAffectCompat = 0;
        bool needRowsAffectedCompat = false;
        cJSON* queryResult = NULL;
        string errorMessage = "unknown";
        int sqliteErrorCode = -1;
        int code = 0;
        string query = queryarr[i];
        long lastTotal = sqlite3_total_changes(m_db);
        queryResult = executeSQLiteStatement(query, jsonParams[i], cbc, code, errorMessage);
        if(queryResult != NULL) {
            long newTotal = sqlite3_total_changes(m_db);
            long rowsAffected = newTotal - lastTotal;
            
            cJSON_AddNumberToObject(queryResult, "rowsAffected", rowsAffected);
            if(rowsAffected > 0) {
                long insertId = sqlite3_last_insert_rowid(m_db);
                if(insertId > 0) {
                    cJSON_AddNumberToObject(queryResult, "insertId", insertId);
                }
            }
                
            cJSON* r = cJSON_CreateObject();
            cJSON_AddStringToObject(r, "type", "success");
            cJSON_AddItemToObject(r, "result", queryResult);
            cJSON_AddItemToArray(batchResults, r);
        } else {
            cJSON* r = cJSON_CreateObject();
            cJSON_AddStringToObject(r, "type", "error");
            
            cJSON* er = cJSON_CreateObject();
            cJSON_AddStringToObject(r, "message", errorMessage.c_str());
            cJSON_AddNumberToObject(r, "code", code);
            
            cJSON_AddItemToObject(r, "result", er);
            
            cJSON_AddItemToArray(batchResults, r);
        }
    }
    cbc.success(batchResults);
    cJSON_Delete(batchResults);
}
cJSON* SQliteConnectorDatabase::executeSQLiteStatement(string query, cJSON* paramsAsJson, CallbackContext cbc, int& rc, string& errMsg)
{
    cJSON* rowsResult = cJSON_CreateObject();
    sqlite3_stmt *ppStmt;
    rc = sqlite3_prepare(m_db, query.c_str(), query.size(), &ppStmt, NULL);
    if(rc != SQLITE_OK) {
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "SQliteConnectorDatabase sqlite3_prepare return %d", rc);
        sqlite3_reset(ppStmt);
        sqlite3_finalize(ppStmt);
        return NULL;
    }
    
    vector<string> vecParams;
    int nArraySize = 0;
    if(paramsAsJson != NULL && paramsAsJson->type == cJSON_Array) {
        nArraySize = cJSON_GetArraySize(paramsAsJson);
    }
    
    for(int i=0; i<nArraySize; i++) {
        cJSON* p = cJSON_GetArrayItem(paramsAsJson, i);
        if(p == NULL || (p->valuestring == NULL && p->type == cJSON_String)) {
            if((rc = sqlite3_bind_null(ppStmt, i+1)) != SQLITE_OK) {
                errMsg = sqlite3_errmsg(m_db);
                sqlite3_reset(ppStmt);
                sqlite3_finalize(ppStmt);
                return NULL;
            }
        } else {
            if(p->type == cJSON_Number) {
                if((rc = sqlite3_bind_double(ppStmt,i+1, p->valuedouble)) != SQLITE_OK) {
                    errMsg = sqlite3_errmsg(m_db);
                    sqlite3_reset(ppStmt);
                    sqlite3_finalize(ppStmt);
                    return NULL;
                }
            } else if(p->type == cJSON_String){
                if((rc = sqlite3_bind_text(ppStmt, i+1, p->valuestring, strlen(p->valuestring), NULL)) != SQLITE_OK) {
                    errMsg = sqlite3_errmsg(m_db);
                    sqlite3_reset(ppStmt);
                    sqlite3_finalize(ppStmt);
                    return NULL;
                }
            } else {
                const char* pValue = cJSON_Print(p);
                if((rc = sqlite3_bind_text(ppStmt, i+1, pValue, strlen(pValue), SQliteConnectorDatabase::freeBindMemory)) != SQLITE_OK) {
                    errMsg = sqlite3_errmsg(m_db);
                    sqlite3_reset(ppStmt);
                    sqlite3_finalize(ppStmt);
                    return NULL;
                }
            }
        }
    }
        
    rc = sqlite3_step(ppStmt);
    if(rc == SQLITE_ROW) {
        cJSON* rowsArrayResult = cJSON_CreateArray();
        string key = "";
        int colCount = sqlite3_column_count(ppStmt);
        do {
            cJSON* row = cJSON_CreateObject();
            for(int i=0; i<colCount; ++i) {
                key = sqlite3_column_name(ppStmt, i);
                switch(sqlite3_column_type(ppStmt, i)) {
                case SQLITE_NULL:
                    cJSON_AddNullToObject(row, key.c_str());
                    break;
                case SQLITE_FLOAT:
                    cJSON_AddNumberToObject(row, key.c_str(), sqlite3_column_double(ppStmt, i));
                    break;
                case SQLITE_INTEGER:
                    cJSON_AddNumberToObject(row, key.c_str(), sqlite3_column_int64(ppStmt, i));
                    break;
                case SQLITE_BLOB:
                case SQLITE_TEXT:
                default:
                    cJSON_AddStringToObject(row, key.c_str(), (const char*)sqlite3_column_text(ppStmt, i));
                    break;
                }
            }
            cJSON_AddItemToArray(rowsArrayResult, row);
        } while ((rc = sqlite3_step(ppStmt)) == SQLITE_ROW);
        cJSON_AddItemToObject(rowsResult, "rows", rowsArrayResult);
    }
    
    if(rc == SQLITE_ERROR) {
        errMsg = sqlite3_errmsg(m_db);
        sqlite3_reset(ppStmt);
        sqlite3_finalize(ppStmt);
        return NULL;
    }
    
    sqlite3_reset(ppStmt);
    sqlite3_finalize(ppStmt);
    return rowsResult;
}

void SQliteConnectorDatabase::freeBindMemory(void* pVoid) {
    char* pMem = (char*)pVoid;
    free(pMem);
}